package app.common.utils.cjt;

import app.common.utils.cjt.ao.CjtResult;
import app.common.utils.cjt.ao.FindVoucherListAo;
import app.common.utils.cjt.ao.FindVoucherListVo;
import app.common.utils.cjt.ao.InventoryCreateAo;
import app.common.utils.cjt.ao.OtherDispatchCreateAo;
import app.common.utils.cjt.ao.OtherReceiveCreateAo;
import app.common.utils.cjt.ao.PartnerCreateAo;
import app.common.utils.cjt.ao.PurchaseArrivalCreateAo;
import app.common.utils.cjt.ao.SaleDeliveryBatchDeleteAo;
import app.common.utils.cjt.ao.SaleDeliveryCreateAo;
import app.common.utils.cjt.ao.SaleDeliveryDeleteAo;
import app.common.utils.cjt.vo.AccessTokenVo;
import app.common.utils.cjt.vo.InventoryClassQueryVo;
import app.common.utils.cjt.vo.OtherReceiveOpenApiFindVoucherListVo;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestRequest;
import com.dtflys.forest.http.ForestRequestType;
import com.dtflys.forest.utils.TypeReference;
import com.gitee.feizns.quickstart.web.ex.ServiceException;
import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <b>畅捷通调用工具类</b>
 * @author feizns
 * @since 2023/3/23
 */
public class CJtUtils {

    {
        String openToken = Forest.get("https://mes.scwkzt.com/api/v2/cjt/open-token").executeAsString();
        resetOpenToken(openToken);
    }

    /**
     * aesKey 消息秘钥
     */
    public static final String aesKey = "1234567890ABCDEF";

    /**
     * appKey
     */
    private static final String appKey = "ItsCwZ6K";

    /**
     * appSecret
     */
    private static final String appSecret = "C917BE3A561ECB82057BEE8AAEF3EB4E";

    /**
     * 用户永久授权码
     */
    private static final String userAuthPermanentCode = "up-98cfeb4f1dfa48dbbccaa4222bbc53e0";

    /**
     * 企业永久授权码
     */
    private static final String permanentAuthCode = "op-1b9bae3c1c4e49c2adfc670bfc64376f";

    /**
     * 应用票据
     */
    private static String apiTicket;

    /**
     * appAccessToken
     */
    @Getter
    private static String appAccessToken;

    /**
     * 企业凭证
     */
    @Getter
    private static String orgAccessToken;

    /**
     * 访问token，请求业务接口的openToken字段
     */
    @Getter
    private static String access_token;

    /**
     * 过期时间（毫秒）
     */
    @Getter
    private static Long expiredTime;

    public static void resetOpenToken(String openToken) {
        access_token = openToken;
    }

    /**
     * 重置开放令牌
     */
    public static void resetOpenToken() {
        if ( accessTokenIsExpired() ) {
            AccessTokenVo vo = Forest.get("http://mes.scwkzt.com/api/v2/cjt/open-token").execute(AccessTokenVo.class);
            access_token = vo.getAccessToken();
            expiredTime = vo.getExpiredTime();
        }
    }

    /**
     * 设置api ticket
     * @param apiTicket api ticket
     */
    public static void setApiTicket(String apiTicket) {
        CJtUtils.apiTicket = apiTicket;
    }

    /**
     * 获取api ticket
     * @return api ticket
     */
    public static String getApiTicket() {
        if ( apiTicket == null )
            authAppTicketResend();
        return apiTicket;
    }

    /**
     * <b>立即触发appTicket推送</b>
     * <p>
     * <a href="https://open.chanjet.com/docs/file/apiFile/common/app_settled/app_settled_app_auth?id=31716#side1">立即触发appTicket推送</a>
     * </p>
     */
    @SneakyThrows
    public static void authAppTicketResend() {
        request(ForestRequestType.POST).path("/auth/appTicket/resend")
                .execute(new TypeReference<CjtResp<String>>() {});
    }

    /**
     * 获取应用凭证
     * @link <a href="https://open.chanjet.com/docs/file/apiFile/common/app_settled/app_settled_app_auth?id=31643">获取应用凭证</a>
     * @return {@link String}
     */
    public static String authAppAuthGetAppAccessToken() {
        if ( apiTicket != null ) {
            //发送请求
            CjtResp<AuthAppAuthGetAppAccessToken> cjtResp = request(ForestRequestType.POST)
                    .path("/auth/appAuth/getAppAccessToken")
                    .addBody("appTicket", apiTicket)
                    .execute(new TypeReference<CjtResp<AuthAppAuthGetAppAccessToken>>() {});
            if ( !cjtResp.isSuccess() )
                throw new ServiceException("请求获取应用凭证失败：" + cjtResp.getMessage());
            appAccessToken = cjtResp.getResult().getAppAccessToken();
            return appAccessToken;
        }
        return null;
    }

    /**
     * 获取企业凭证
     * @link <a href="https://open.chanjet.com/docs/file/apiFile/common/app_settled/app_settled_app_auth?id=31644">获取企业凭证</a>
     * @return {@link String}
     */
    public static String authOrgAuthGetOrgAccessToken() {
        if ( appAccessToken != null ) {
            CjtResp<AuthOrgAuthGetOrgAccessTokenResult> resp = request(ForestRequestType.POST)
                    .path("/auth/orgAuth/getOrgAccessToken")
                    .addBody("permanentAuthCode", permanentAuthCode)
                    .addBody("appAccessToken", appAccessToken)
                    .execute(new TypeReference<CjtResp<AuthOrgAuthGetOrgAccessTokenResult>>(){});
            if ( !resp.isSuccess() )
                throw new ServiceException("获取企业凭证失败: " + resp.getMessage());
            orgAccessToken = resp.getResult().getOrgAccessToken();
            return orgAccessToken;
        }
        return null;
    }

    /**
     * 使用用户永久授权码获取token
     * @link <a href="https://open.chanjet.com/docs/file/apiFile/common/app_settled/app_settled_auth?id=31645">使用用户永久授权码获取token</a>
     * @return {@link String}
     */
    public static String authTokenGetTokenByPermanentCode() {
        if ( orgAccessToken != null ) {
            CjtResp<AuthTokenGetTokenByPermanentCodeResult> resp = request(ForestRequestType.POST)
                    .path("/auth/token/getTokenByPermanentCode")
                    .addBody("orgAccessToken", orgAccessToken)
                    .addBody("userAuthPermanentCode", userAuthPermanentCode)
                    .execute(new TypeReference<CjtResp<AuthTokenGetTokenByPermanentCodeResult>>() {
                    });
            if ( !resp.isSuccess() )
                throw new ServiceException("使用用户永久授权码获取token失败: " + resp.getMessage());
            access_token = resp.getResult().getAccess_token();
            expiredTime = resp.getResult().expiredTimeMillis();
            return access_token;
        }
        return null;
    }

    /**
     * 畅捷通接口访问
     * @return {@link ForestRequest}
     */
    public static ForestRequest<?> request(ForestRequestType type) {
        return Forest.request().setBasePath("https://openapi.chanjet.com")
                .setReadTimeout(60000)
                .setType(type)
                .addHeader("appKey", appKey)
                .addHeader("appSecret", appSecret)
                .contentTypeJson();
    }

    /**
     * 畅捷通接口访问
     * @return {@link ForestRequest}
     */
    public static ForestRequest<?> openTokenRequest(ForestRequestType type) {
        resetOpenToken();
        return request(type).addHeader("openToken", access_token);
    }

    /**
     * accessToken是否已过期
     * @return 是/否
     */
    public static boolean accessTokenIsExpired() {
        return expiredTime == null || expiredTime < System.currentTimeMillis();
    }

    /**
     * 其他出库单创建
     * @param dto dto
     */
    public static String otherDispatchCreate(OtherDispatchCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/otherDispatch/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 其他入库单创建
     * @param dto dto
     */
    public static String otherReceiveCreate(OtherReceiveCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/otherReceive/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 其他入库单删除
     * @param externalCode 外部编码
     */
    public static String otherReceiveOpenApiDelete(String externalCode) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/OtherReceiveOpenApi/Delete")
                .addBody("param", Map.of("ExternalCode", externalCode))
                .executeAsString();
    }

    /**
     * 其他出库单删除
     * @param externalCode 外部代码
     * @return {@link String}
     */
    public static String otherDispatchOpenApiDelete(String externalCode) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/OtherDispatchOpenApi/Delete")
                .addBody("param", Map.of("ExternalCode", externalCode))
                .executeAsString();
    }

    /**
     * 销货单创建
     * @param dto dto
     * @return {@link String}
     */
    public static String saleDeliveryCreate(SaleDeliveryCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/saleDelivery/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 销货单删除
     * @param ao ao
     * @return {@link String}
     */
    public static String saleDeliveryDelete(SaleDeliveryDeleteAo ao) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/SaleDeliveryOpenApi/Delete")
                .addBody(ao)
                .executeAsString();
    }

    /**
     * 销货单删除
     * @param ao ao
     * @return {@link String}
     */
    public static String saleDeliveryBatchDelete(SaleDeliveryBatchDeleteAo ao) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/saleDelivery/DeleteBatch")
                .addBody(ao)
                .executeAsString();
    }

    /**
     * 进货单创建
     * @param dto dto
     * @return {@link String}
     */
    public static String purchaseArrivalCreate(PurchaseArrivalCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/purchaseArrival/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 存货创建
     * @param dto dto
     * @return {@link String}
     */
    public static String inventoryCreate(InventoryCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/inventory/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 往来单位创建
     * @param dto dto
     * @return {@link String}
     */
    public static String partnerCreate(PartnerCreateAo.Dto dto) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/partner/Create")
                .addBody("dto", dto)
                .executeAsString();
    }

    /**
     * 其他入库单列表
     * @return {@link String}
     */
    public static List<String> otherReceiveOpenApiFindVoucherList(int index, int size) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/OtherReceiveOpenApi/FindVoucherList")
                .addBody("pageIndex", index)
                .addBody("pageSize", size)
                .addBody("selectFields", List.of("RDRecord.ExternalCode"))
                .addBody("paramDic", Map.of("RDRecord.VoucherDate", Arrays.asList(
                        Map.of("text", "2023-05-17", "value", "2023-05-17")
                        ,Map.of("text", "2023-07-31", "value", "2023-07-31")
                )
                        ,"RDRecord.Memo", List.of(Map.of("text", "来源：[旺店通]", "value", "来源：[旺店通]"))
                ))
                .execute(new TypeReference<CjtResult<OtherReceiveOpenApiFindVoucherListVo>>() {})
                .getData().columns(0);
    }

    /**
     * 其他入库单
     * @return {@link String}
     */
    public static List<String> otherDispatchOpenApiFindVoucherList(int index, int size) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/OtherDispatchOpenApi/FindVoucherList")
                .addBody("pageIndex", index)
                .addBody("pageSize", size)
                .addBody("selectFields", List.of("RDRecord.ExternalCode"))
                .addBody("paramDic", Map.of("RDRecord.VoucherDate", Arrays.asList(
                        Map.of("text", "2024-01-01", "value", "2024-01-01")
                        ,Map.of("text", "2024-01-25", "value", "2024-01-25")
                )
                        , "RDRecord.Memo", List.of(Map.of("text", "来源：[旺店通]", "value", "来源：[旺店通]"))
                ))
                .execute(new TypeReference<CjtResult<OtherReceiveOpenApiFindVoucherListVo>>() {})
                .getData().columns(0);
    }

    /**
     * 存货分类查询
     * @return {@link String}
     */
    @SuppressWarnings("unchecked")
    public static List<InventoryClassQueryVo> inventoryClassQuery() {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/inventoryClass/Query")
                .addBody("param", Map.of("SelectFields", "code,name,parent.code,parent.name"))
                .executeAsList()
                .stream()
                .map(o -> (Map<String, Object>) o)
                .map(InventoryClassQueryVo::from)
                .collect(Collectors.toList());
    }

    /**
     * 存货分类创建
     * @param vo 响应对象
     * @return {@link InventoryClassQueryVo}
     */
    public static String inventoryClassCreate(InventoryClassQueryVo vo) {
        return CJtUtils.openTokenRequest(ForestRequestType.POST)
                .path("/tplus/api/v2/inventoryClass/Create")
                .addBody("dto",
                        Map.of(
                                "Code", vo.getCode(),
                                "Name", vo.getName(),
                                "Parent", Map.of(
                                        "Code",vo.getParentCode(),
                                        "Name",vo.getParentName()
                                )
                        )
                )
                .executeAsString();
    }

    /**
     * 找到凭证列表
     * @return {@link String}
     */
    public static List<String> findVoucherList(FindVoucherListAo ao) {
        resetOpenToken();
        return findVoucherList(ao, access_token);
    }

    /**
     * 找到凭证列表
     * @return {@link String}
     */
    public static List<String> findVoucherList(FindVoucherListAo ao, String openToken) {
        CjtResult<FindVoucherListVo> execute = CJtUtils.request(ForestRequestType.POST)
                .addHeader("openToken", openToken)
                .path("/tplus/api/v2/SaleDeliveryOpenApi/FindVoucherList")
                .addBody(ao)
                .execute(new TypeReference<>() {});
        return execute.getData().getColumnData("code");
    }

    /**
     * 找到凭证列表
     * @param start 开始
     * @param end 结束
     * @return {@link List}<{@link String}>
     */
    public static List<String> findVoucherList(LocalDate start, LocalDate end) {
        return findVoucherList(FindVoucherListAo.ofDefault(start, end));
    }

    /**
     * 删除销售交付
     */
    public static void deleteSalesDelivery(LocalDate startDate, LocalDate endDate) {
        FindVoucherListAo ao = FindVoucherListAo.ofDefault(startDate, endDate);
        List<String> ids = findVoucherList(ao);
        while ( !CollectionUtils.isEmpty(ids) ) {
            System.out.println(ids);
            for (String id : ids) {
                try {
                    CJtUtils.saleDeliveryDelete(SaleDeliveryDeleteAo.of(id));
                } catch (Exception e) {
                    if ( e.getMessage().equals("生效状态的销货单，不能进行此操作！") ) {
                        //忽略异常
//                        e.printStackTrace();
                    }
                    e.printStackTrace();
                }
            }
            ids = findVoucherList(ao);
        }
    }

    public static void main(String[] args) {
//        deleteSalesDelivery(LocalDate.of(2023, 8, 7), LocalDate.of(2023, 9, 21));
//        inventoryClassQuery().forEach(System.out::println);
//        deleteOtherDispatch(1000);
        String delete = otherDispatchOpenApiDelete("CK20240126044500000-311130203");
        //CjtResp<OtherReceiveOpenApiFindVoucherListVo>
//        System.out.println(otherReceiveOpenApiFindVoucherList(0, 1000));

//        System.out.println(otherDispatchOpenApiFindVoucherList(0, 1000));
//        LocalDate startDate = LocalDate.of(2023, 7, 7);
//        LocalDate endDate = LocalDate.of(2023, 7, 7);
//        FindVoucherListAo ao = FindVoucherListAo.ofDefault(startDate, endDate);
//        System.out.println(findVoucherList(ao));
//        deleteSalesDelivery(startDate, endDate);
    }

    public static class Test {
        public static void main(String[] args) {
            deleteOtherReceive(1000);
//            dele(1000);
//            List<String> codes = new ArrayList<>();
//            int index = 0;
//            do {
//                otherReceiveOpenApiFindVoucherList(index, 100000);
//            } while ();
        }
    }

    /**
     * 删除其他出库单
     */
    public static void deleteOtherDispatch(int limit) {
        int index = 0;
        List<String> codes;
        do {
            codes = otherDispatchOpenApiFindVoucherList(index, limit);
            codes.forEach(CJtUtils::otherDispatchOpenApiDelete);
        } while ( codes.size() == limit );
    }

    /**
     * 删除其他入库单
     */
    public static void deleteOtherReceive(int limit) {
        int index = 0;
        List<String> codes;
        do {
            codes = otherReceiveOpenApiFindVoucherList(index, limit);
            codes.forEach(CJtUtils::otherReceiveOpenApiDelete);
        } while ( codes.size() == limit );
    }

}
